spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
1import { api } from "@/lib/api";2import { SITE_URL } from "@/lib/routes";3import { KIND_FREQ, KIND_HREF, STATIC_PAGES, parseSitemapId } from "../../sitemap.xml/ids";45/**6 * Per-id sitemaps at /sitemap/<id>.xml — `static` for the fixed pages, `<kind>-<page>` for each entity kind7 * (5 000 URLs per API page, see /api/v1/sitemap/:kind). Listed by the index at /sitemap.xml.8 * Hand-rolled instead of Next's `generateSitemaps` because that convention also claims /sitemap.xml itself9 * (and crashes there without an id), which robots.txt points to.10 */11export const revalidate = 3600;1213const esc = (s: string) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);1415interface Entry {16 url: string;17 lastModified?: Date;18 changeFrequency?: string;19 priority?: number;20}2122function toXml(entries: Entry[]): string {23 const body = entries24 .map((e) => {25 const parts = [`<loc>${esc(e.url)}</loc>`];26 if (e.lastModified) parts.push(`<lastmod>${e.lastModified.toISOString()}</lastmod>`);27 if (e.changeFrequency) parts.push(`<changefreq>${e.changeFrequency}</changefreq>`);28 if (e.priority !== undefined) parts.push(`<priority>${e.priority}</priority>`);29 return ` <url>${parts.join("")}</url>`;30 })31 .join("\n");32 return `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${body}\n</urlset>\n`;33}3435export async function GET(_req: Request, ctx: { params: Promise<{ id: string }> }): Promise<Response> {36 const { id } = await ctx.params;37 if (!id.endsWith(".xml")) return new Response("Not Found", { status: 404 });38 const parsed = parseSitemapId(id.slice(0, -4));39 if (!parsed) return new Response("Not Found", { status: 404 });4041 let entries: Entry[];42 if (parsed === "static") {43 const now = new Date();44 entries = STATIC_PAGES.map((p) => ({ url: `${SITE_URL}${p.path}`, lastModified: now, changeFrequency: p.changeFrequency, priority: p.priority }));45 } else {46 const r = await api.sitemap(parsed.kind, parsed.page);47 if (!r.ok && r.status !== 404) return new Response("Service Unavailable", { status: 503, headers: { "Retry-After": "300" } });48 const href = KIND_HREF[parsed.kind];49 const priority = parsed.kind === "facilities" || parsed.kind === "countries" ? 0.7 : 0.6;50 entries = (r.ok && Array.isArray(r.data) ? r.data : []).map((e) => {51 const d = new Date(e.updatedAt);52 return { url: `${SITE_URL}${href(e.slug)}`, lastModified: Number.isNaN(d.getTime()) ? undefined : d, changeFrequency: KIND_FREQ[parsed.kind], priority };53 });54 }55 return new Response(toXml(entries), { headers: { "Content-Type": "application/xml; charset=utf-8", "Cache-Control": "public, max-age=3600, s-maxage=3600, stale-while-revalidate=86400" } });56}57